package util;

import cn.hutool.core.swing.RobotUtil;
import cn.hutool.core.util.ObjectUtil;
import org.apache.commons.io.FileUtils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.Raster;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;

public class SomeUtil {

    //下载路径
    public static final String dirPath = "D:\\ChromeDown\\downloadimage";

    /**
     * @param videoPath
     * @param audioPath
     * @param outPut
     * @return
     * @throws Exception
     */
    public static boolean mergeAudioAndVideo(String videoPath, String audioPath, String outPut) throws Exception {
        boolean isCreated = true;
        //输入为空直接返回
        File file = new File(videoPath);
        if (!file.exists()) {
            return false;
        }
        //录制对象
        FrameRecorder recorder = null;
        //抓取视频对象
        FrameGrabber grabberVideo = null;
        //抓取音频对象
        FrameGrabber grabberAudio = null;
        try {
            //抓取视频帧
            grabberVideo = new FFmpegFrameGrabber(videoPath);
            int lengthInFramesVideo = grabberVideo.getLengthInFrames();
            grabberVideo.start();
            //抓取音频帧
            grabberAudio = new FFmpegFrameGrabber(audioPath);
            int lengthInFramesAudio = grabberAudio.getLengthInFrames();
            grabberAudio.start();
            //创建录制
            recorder = new FFmpegFrameRecorder(outPut, grabberVideo.getImageWidth(), grabberVideo.getImageHeight(), grabberAudio.getAudioChannels());
            recorder.setFormat("mp4");
            recorder.setFrameRate(grabberVideo.getFrameRate());
            recorder.setSampleRate(grabberAudio.getSampleRate());
            recorder.start();

            int loopAudioTimes = 1;
            if(lengthInFramesVideo > lengthInFramesAudio){
                loopAudioTimes = lengthInFramesVideo % lengthInFramesAudio == 0 ? lengthInFramesVideo / lengthInFramesAudio : lengthInFramesVideo / lengthInFramesAudio +1;
            }else{
                loopAudioTimes = lengthInFramesVideo;
            }
            //先录入视频
            Frame frameVideo;
            while ((frameVideo = grabberVideo.grabFrame()) != null) {
                recorder.record(frameVideo);
            }

            //然后录入音频
            Frame frameAudio;
            while ((frameAudio = grabberAudio.grabFrame()) != null) {
                recorder.record(frameAudio);
            }

            grabberVideo.stop();
            grabberAudio.stop();
            recorder.stop();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (recorder != null) {
                    recorder.release();
                }
                if (grabberVideo != null) {
                    grabberVideo.release();
                }
                if (grabberAudio != null) {
                    grabberAudio.release();
                }
            } catch (FrameRecorder.Exception e) {
                e.printStackTrace();
            }
        }
        return isCreated;

    }

    /**
     * 切割文件流数据
     *
     * @param inFile  输入流对象
     * @param bt      歌曲的比特率
     * @param start   裁剪的起始时间
     * @param end     裁剪结束时间
     * @param outFile 输出流对象
     * @throws IOException
     */
    public static void cutAudio(FileInputStream inFile, int bt, int start, int end, FileOutputStream outFile) throws IOException {
        //计算开始截取的字节
        int startByte = bt * start * 1024 / 8;
        //计算结束截取的字节
        int endByte = bt * end * 1024 / 8;
        //定义接收到几个字符长度变量len和total累加器
        int len, total = 0;
        //定义接收字节的byte数组
        byte[] bz = new byte[1024];

        while ((len = inFile.read(bz)) != -1) {
            //累加接收到的个数
            total += len;
            //判断字节是否小于要截取的字节数
            if (total < startByte){
                continue;
            }
            //判断字节是否大于要截取的字节数，大于则跳出循环
            if (total > endByte){
                break;
            }
            //将bz数组中的字节输出到fos
            outFile.write(bz, 0, len);
        }
        System.out.println("裁剪完成");
    }

    /**
     * 图片转视频
     *
     * @param mp4SavePath 视频另存为的地址
     * @param imgPath     原图片文件夹的地址
     * @param imgWidth    图片宽
     * @param imgHeight   图片高
     * @param frameRate   帧率
     * @throws FrameRecorder.Exception
     */
    public static void img2Mp4(String mp4SavePath, String imgPath, int imgWidth, int imgHeight, double frameRate, String prefix, String suffix) throws FrameRecorder.Exception {
        //读取所有图片
        File file = new File(imgPath);
        File[] files = file.listFiles();
        Collections.sort(Arrays.asList(files), new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                Integer newFileName1 = Integer.parseInt(o1.getName().replace(prefix, "").replace(suffix, ""));
                Integer newFileName2 = Integer.parseInt(o2.getName().replace(prefix, "").replace(suffix, ""));
                return newFileName1 - newFileName2;
            }
        });
        Map<Integer, File> imgMap = new HashMap<Integer, File>();
        int num = 0;
        for (File imgFile : files) {
            imgMap.put(num, imgFile);
            num++;
        }
        //创建mp4
        SomeUtil.createMp4(mp4SavePath, imgMap, imgWidth, imgHeight, frameRate);
    }

    /**
     * 创建mp4
     *
     * @param mp4SavePath
     * @param imgMap
     * @param width
     * @param height
     * @throws FrameRecorder.Exception
     */
    public static void createMp4(String mp4SavePath, Map<Integer, File> imgMap, int width, int height, double frameRate) throws FrameRecorder.Exception {
        //视频宽高最好是按照常见的视频的宽高  16：9  或者 9：16
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(mp4SavePath, width, height);
        //设置视频编码层模式
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        //设置视频为25帧每秒
//        recorder.setFrameRate(frameRate);
        //设置视频图像数据格式
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        recorder.setFormat("mp4");
        try {
            recorder.start();
            Java2DFrameConverter converter = new Java2DFrameConverter();
            //录制一个22秒的视频
            for (int i = 0; i < imgMap.size()/ frameRate; i++) {
                // <a href="https://www.cnblogs.com/tomcatandjerry/" rel="noopener">https://www.cnblogs.com/tomcatandjerry/<br></a>
                BufferedImage read = readImageWithNoError(imgMap.get(i));
                System.out.println(imgMap.get(i).getName());
                //出现读取文件错误问题
                //BufferedImage read = ImageIO.read(imgMap.get(i));
                //一秒是25帧 所以要记录25次
                for (int j = 0; j < 25 * 4; j++) {
                    recorder.record(converter.getFrame(read));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //最后一定要结束并释放资源
            recorder.stop();
            recorder.release();
        }
    }

    /**
     * @param webDriver
     * @param wheelAmt
     * @param randomSeed 随机秒
     * @throws InterruptedException
     */
    public static void robotMouseWheel(Long height, WebDriver webDriver, int wheelAmt, Long randomSeed) throws InterruptedException {
        //高度
        if (ObjectUtil.isNull(height) || height == 0L) {
            height = (long) ((JavascriptExecutor) webDriver).executeScript("return document.documentElement.scrollHeight");
        }
        //一个滚轮80像素
        int splitNum = 80 * wheelAmt;
        int pageNum = (int) (height % splitNum == 0 ? height / splitNum : height / splitNum + 1);
        for (int i = 1; i < pageNum + 1; i++) {
            System.out.println("++++++++++++" + (i * splitNum) + "+++++++++++++++");
            //wheelAmt代表滚轮一个卡槽 80像素
            RobotUtil.setDelay(Math.toIntExact(randomSeed));
            RobotUtil.mouseWheel(wheelAmt);
            //临时休眠
            Thread.sleep(randomSeed);
        }
    }

    /**
     * 代码定位 进行下滑
     *
     * @param webDriver
     * @param splitNum  滚轮一次分割的像素
     * @throws InterruptedException
     */
    public static void windowScrollTo(WebDriver webDriver, int splitNum) throws InterruptedException {
        //高度
        long height = (long) ((JavascriptExecutor) webDriver).executeScript("return document.documentElement.scrollHeight");
        System.out.println("====================" + height);
        //1080 - 142 - 40 = 898 900
        int pageNum = (int) (height % splitNum == 0 ? height / splitNum : height / splitNum + 1);
        for (int i = 1; i < pageNum + 1; i++) {
            String executeScript = "";
            if (i == pageNum) {
                //下拉到页面指定位置
                executeScript = "window.scrollTo(" + ((int) (i - 1) * splitNum) + "," + ((int) height - (i * splitNum)) + ")";
            } else {
                executeScript = "window.scrollTo(" + (i - 1) * splitNum + "," + i * splitNum + ")";
            }
            System.out.println("====================" + executeScript);
            ((JavascriptExecutor) webDriver).executeScript(executeScript);
            //表达式获取数据
//            WebElement element = webDriver.findElement(By.xpath(""));
            Thread.sleep(2000);
        }

    }

    /**
     * 多线程处理
     *
     * @param executorService
     */
    public static void processSync(ExecutorService executorService, List<String> imageUrls) {
        File file = new File(dirPath);
        file.mkdirs();
        for (int i = 0; i < imageUrls.size(); i++) {
            String imagePath = dirPath + "\\" + "image" + i + ".jpg";
            String imageUrl = imageUrls.get(i);
            executorService.execute(() -> downloaderMothod(imageUrl, imagePath));
        }
        executorService.shutdown();
    }

    /**
     * 文件排序
     *
     * @param s
     * @return
     */
    public static File[] sort(File[] s, String prefix) {
        //中间值
        File temp = null;
        //外循环:我认为最小的数,从0~长度-1
        for (int j = 0; j < s.length - 1; j++) {
            //最小值:假设第一个数就是最小的
            String min = s[j].getName();
            //记录最小数的下标的
            int minIndex = j;
            //内循环:拿我认为的最小的数和后面的数一个个进行比较
            for (int k = j + 1; k < s.length; k++) {
                //找到最小值
                if (Integer.parseInt(
                        min.replace(prefix, "")
                                .substring(
                                        0,
                                        min.replace(prefix, "").indexOf(".")
                                )
                ) >
                        Integer.parseInt(
                                s[k].getName().replace(prefix, "")
                                        .substring(
                                                0,
                                                s[k].getName().replace(prefix, "").indexOf(".")
                                        )
                        )
                ) {
                    //修改最小
                    min = s[k].getName();
                    minIndex = k;
                }
            }
            //当退出内层循环就找到这次的最小值
            //交换位置
            temp = s[j];
            s[j] = s[minIndex];
            s[minIndex] = temp;
        }
        return s;
    }

    /**
     * 下载方法
     *
     * @param url
     * @param name
     */
    public static void downloaderMothod(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.fillInStackTrace();
            System.out.println("IO异常，downloaderMothod方法异常");
        }
    }

    /**
     * 设置图片类型
     *
     * @return
     */
    public static List<String> imageType() {
        //图片类型
        List<String> imageTYpe = new ArrayList<>();
        imageTYpe.add("image/jpeg");
        imageTYpe.add("image/jpg");
        imageTYpe.add("image/png");
        imageTYpe.add("image");
        imageTYpe.add("png");
        imageTYpe.add("jpg");
        imageTYpe.add("jpeg");
        return imageTYpe;
    }

    /**
     * 批量修改文件名
     *
     * @param path
     * @param prefix
     * @param suffix
     */
    public static void editBatchFileName(String path, String prefix, String suffix) {
        File folder = new File(path);
        if (folder.exists()) {
            File[] fileArr = folder.listFiles();
            if (null == fileArr || fileArr.length == 0) {
                return;
            } else {
                //文件所在文件夹路径+新文件名
                File newDir = null;
                //新文件名
                String newName = "";
                //旧文件名
                String fileName = null;
                //文件所在父级路径
                File parentPath = new File("");
                for (File file : fileArr) {
                    //是文件夹，继续递归，如果需要重命名文件夹，这里可以做处理
                    if (file.isDirectory()) {
                        editBatchFileName(file.getAbsolutePath(), prefix, suffix);
                        //是文件，判断是否需要重命名
                    } else {
                        fileName = file.getName();
                        parentPath = file.getParentFile();
                        //文件名包含需要被替换的字符串
                        if (fileName.contains(prefix)) {
                            //新名字
                            newName = fileName.replaceAll("^(0+)", "")
                                    .replaceAll(prefix, "")
                                    .replace(suffix, "");
                            if (Integer.parseInt(newName) < 10 && Integer.parseInt(newName) > 0) {
                                //文件所在文件夹路径+新文件名
                                newDir = new File(parentPath + "/" + "000" + newName + suffix);
                            } else if (Integer.parseInt(newName) < 100 && Integer.parseInt(newName) > 10) {
                                //文件所在文件夹路径+新文件名
                                newDir = new File(parentPath + "/" + "00" + newName + suffix);
                            } else if (Integer.parseInt(newName) < 1000 && Integer.parseInt(newName) > 100) {
                                //文件所在文件夹路径+新文件名
                                newDir = new File(parentPath + "/" + "0" + newName + suffix);
                            } else {
                                //文件所在文件夹路径+新文件名
                                newDir = new File(parentPath + "/" + newName + suffix);
                            }
                            //重命名
                            file.renameTo(newDir);
                            System.out.println("修改后：" + newDir);
                        }
                    }
                }
            }
        } else {
            System.out.println("文件不存在!");
        }
    }

    /**
     * 解决ImageIO.read 报错问题 : https://www.cnblogs.com/tomcatandjerry/p/12368125.html
     *
     * @param imageFile
     * @return
     */
    public static BufferedImage readImageWithNoError(File imageFile) {
        try {
            return ImageIO.read(imageFile);
        } catch (Exception e) {
            try {
                //Find a suitable ImageReader
                Iterator readers = ImageIO.getImageReadersByFormatName("JPEG");
                ImageReader reader = null;
                while (readers.hasNext()) {
                    reader = (ImageReader) readers.next();
                    if (reader.canReadRaster()) {
                        break;
                    }
                }
                //Stream the image file (the original CMYK image)
                ImageInputStream input;
                input = ImageIO.createImageInputStream(imageFile);
                if (input == null) {
                    System.out.println("+++++++++ImageIO.createImageInputStream return null++++++++++");
                    return null;
                }
                reader.setInput(input);
                //Read the image raster
                Raster raster = reader.readRaster(0, null);
                //Create a new RGB image
                BufferedImage bi = new BufferedImage(raster.getWidth(), raster.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
                //Fill the new image with the old raster
                bi.getRaster().setRect(raster);
                BufferedImage greyImage = makeGrey(bi);
                return greyImage;
            } catch (Exception e1) {
                System.out.println("+++++++++readImageWithNoError: error++++++++++");
                return null;
            }
        }
    }

    /**
     * 对图片做灰化处理
     *
     * @param img
     * @return
     */
    public static BufferedImage makeGrey(BufferedImage img) {
        //更换图片图层： <a href="https://www.cnblogs.com/tomcatandjerry/" rel="noopener">https://www.cnblogs.com/tomcatandjerry/</a>
        BufferedImage bufferedImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
        bufferedImage.getGraphics().drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null);
        bufferedImage = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null).filter(img, bufferedImage);
        return bufferedImage;
    }

    /**
     * 视频类型
     * @param path
     * @return
     */
    public static List<String> isVideo(String path) {
        //设置视频后缀
        List<String> typeList = new ArrayList<String>();
        typeList.add("mp4");
        typeList.add("flv");
        typeList.add("avi");
        typeList.add("rmvb");
        typeList.add("rm");
        typeList.add("wmv");
        return typeList;
    }

}
